#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <windows.h>
#include <wincrypt.h>
#include <utils.h>

// 获取长度为buffer_len的字符串
int gen_random_bytes(uint8_t *buffer, const size_t buffer_len) {
    int ret = -1;
    if (!buffer || (buffer_len > INT_MAX)) {
        print_log();
        return ret;
    }
    HCRYPTPROV hCryptProv;
    if (CryptAcquireContextA(&hCryptProv, NULL, NULL, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != TRUE) {
        print_log();
        return ret;
    }

    if (CryptGenRandom(hCryptProv, (DWORD) buffer_len, buffer) != TRUE) {
        print_log();
        goto clean;
    }
    ret = 1;
clean:
    if (CryptReleaseContext(hCryptProv, 0) != TRUE) {
        print_log();
        ret = -1;
    }
    return ret;
}


int sm2_printf( const char *chars, ...) {
    va_list args;
    va_start(args, chars);
    vprintf( chars, args);
    va_end(args);
    return 1;
}


int sm2_printf_bytes(const char *chars, const uint8_t *data, const size_t data_len) {
    if (data_len > 1 << 24) {
        print_log();
        return -1;
    }
    printf( "%s: ", chars);
    for (size_t i = 0; i < data_len; i++) {
        printf( "%02X", data[i]);
    }
    printf("\n");
    return 1;
}

int asn1_length_2_der(const size_t len, uint8_t **out, size_t *out_len) {
    if (len > INT_MAX) {
        print_log();
        return -1;
    }
    if (!out_len) {
        print_log();
        return -1;
    }

    if (len < 128) {
        if (out && *out) {
            *(*out)++ = (uint8_t) len;
        }
        (*out_len)++;
    } else {
        uint8_t buf[4];
        int n_bytes;
        if (len < 256) n_bytes = 1;
        else if (len < 65536) n_bytes = 2;
        else if (len < (1 << 24)) n_bytes = 3;
        else n_bytes = 4;
        PUT_U32(buf, (uint32_t)len);
        if (out && *out) {
            *(*out)++ = 0x80 + n_bytes;
            memcpy(*out, buf + 4 - n_bytes, n_bytes);
            *out += n_bytes;
        }
        *out_len += 1 + n_bytes;
    }
    return 1;
}

int asn1_header_to_der(const size_t d_len, uint8_t **out, size_t *out_len) {
    if (!out_len) {
        print_log();
        return -1;
    }
    if (out && *out) {
        const int tag = 0x20;
        *(*out)++ = (uint8_t) tag;
    }
    (*out_len)++;
    (void) asn1_length_2_der(d_len, out, out_len);
    return 1;
}

int asn1_integer_2_der_ex( const uint8_t *d, size_t d_len, uint8_t **out, size_t *out_len) {
    const int tag = 2;
    if (!out_len) {
        print_log();
        return -1;
    }
    if (!d) {
        return 0;
    }
    if (d_len <= 0 || d_len > INT_MAX) {
        print_log();
        return -1;
    }

    if (out && *out)
        *(*out)++ = tag;
    (*out_len)++;

    while (*d == 0 && d_len > 1) {
        d++;
        d_len--;
    }

    if (d[0] & 0x80) {
        asn1_length_2_der(d_len + 1, out, out_len);
        if (out && *out) {
            *(*out)++ = 0x00;
            memcpy(*out, d, d_len);
            (*out) += d_len;
        }
        *out_len += 1 + d_len;
    } else {
        asn1_length_2_der(d_len, out, out_len);
        if (out && *out) {
            memcpy(*out, d, d_len);
            *out += d_len;
        }
        *out_len += d_len;
    }
    return 1;
}

static int char2int(const char c) {
    if ('0' <= c && c <= '9') return c - '0';
    if ('a' <= c && c <= 'f') return c - 'a' + 10;
    if ('A' <= c && c <= 'F') return c - 'A' + 10;
    return -1;
}

int hex2bytes(const char *in, const size_t in_len, uint8_t *out, size_t *out_len) {
    if (in_len % 2) {
        print_log();
        return -1;
    }
    *out_len = in_len / 2;
    size_t in_len_tmp = in_len;
    while (in_len_tmp) {
        int c;
        if ((c = char2int(*in++)) < 0) {
            print_log();
            return -1;
        }
        *out = (uint8_t) c << 4;

        if ((c = char2int(*in++)) < 0) {
            print_log();
            return -1;
        }
        *out |= (uint8_t) c;
        in_len_tmp -= 2;
        out++;
    }
    return 1;
}
